import threading
from . import reactor


class Deferred(object):
    __slots__ = ["_callbacks", "_stale", "isexc", "value"]
    def __init__(self, value = None):
        self._callbacks = set()
        self._stale = False
        self.isexc = None
        self.value = value
    def register(self, cb):
        if self._stale:
            reactor.register(cb, self)
        else:
            self._callbacks.add(cb)
    def unregister(self, cb):
        self._callbacks.discard(cb)
    def set(self, isexc, value):
        if self._stale:
            raise ValueError("already set")
        self._stale = True
        deferred_signal.signal()
        ready_deferreds.add(self)
    def _invoke(self):
        for cb in self._callbacks:
            cb(self)
        self.callbacks.clear()

def _deferred_invoker():
    for d in ready_deferreds:
        d._invoke()
    ready_deferreds.clear()
    return True

ready_deferreds = set()
deferred_signal = reactor.register_signal(_deferred_invoker)

def defer_to_thread(func, args = (), kwargs = {}):
    res = Deferred()
    def bgcaller(func = func, args = args, kwargs = kwargs, res = res):
        value = None
        isexc = False
        try:
            value = func(*args, **kwargs)
        except Exception, ex:
            value = ex
            isexc = True
        finally:
            res.set(isexc, value)
    t = threading.Thread(target = bgcaller)
    t.start()
    return res

def bgthread(func):
    def wrapper(*args, **kwargs):
        return defer_to_thread(func, args, kwargs)
    return bgfunc

class ReactiveReturn(BaseException):
    def __init__(self, value):
        self.value = value

def reactive_return(value):
    raise ReactiveReturn(value)

def reactive(generator):
    def wrapper(*args, **kwargs):
        def stepper(dfr):
            try:
                if dfr.isexc:
                    dfr2 = it.throw(dfr.value)
                else:
                    dfr2 = it.send(dfr.value)
            except StopIteration:
                result.set(False, None)
            except ReactiveReturn, ex:
                result.set(False, ex.value)
            else:
                dfr2.register(stepper)
            finally:
                return False
        
        result = Deferref()
        it = generator(*args, **kwargs)
        try:
            dfr = it.next()
        except StopIteration:
            result.set(False, None)
        except ReactiveReturn, ex:
            result.set(False, ex.value)
        else:
            dfr.register(stepper)
        return result
    return wrapper 
















